﻿using Prism.Ioc;
using StarryEdge.Core;
using StarryEdge.Core.Device;
using StarryEdge.Core.Enums;
using StarryEdge.Core.Manager;
using StarryEdge.Core.Models;
using StarryEdge.Core.Models.Interact;
using StarryEdge.Core.Parameter;
using StarryEdge.Core.Tool;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace StarryEdge.Parameter
{
    public class DeviceInfoManage : InitFileBase, StarryEdge.Core.IInitFile, IDeviceInfoManage
    {
        public ObservableCollection<DeviceInfoModel> DeviceInfos { get; set; }
        private ILangProvider _langProvider;
        private IContainerProvider _containerProvider;

        public DeviceInfoManage(IStarryEdgeLog edgeLog, ILangProvider langProvider, IContainerProvider containerProvider) : base(edgeLog, "init", "deviceinfo")
        {
            _containerProvider = containerProvider;
            _langProvider = langProvider;
            Init();
        }

        public void Init()
        {
            if (File.Exists(_path))
            {
                string str = base.Read();
                if (!string.IsNullOrEmpty(str))
                {
                    Dictionary<string, object> keyValues = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(str);
                    try
                    {
                        DeviceInfos = System.Text.Json.JsonSerializer.Deserialize<ObservableCollection<DeviceInfoModel>>(keyValues["DeviceInfos"].ToString());
                    }
                    catch (Exception ex)
                    {
                        _runLog.LogRun($"设备信息初始化异常：{ex}", Core.Enums.LogLevel.Error);
                    }
                }
            }
            if (DeviceInfos == null)
            {
                DeviceInfos = new ObservableCollection<DeviceInfoModel>();
            }
            Task.Run(() =>
            {
                Ping ping = new Ping();
                IDeviceFactory deviceFactory = _containerProvider.Resolve<IDeviceFactory>();
                ITaskManager taskManager = _containerProvider.Resolve<ITaskManager>();
                ITemporaryParameter temporaryParameter = _containerProvider.Resolve<TemporaryParameter>();
                while (true)
                {
                    if (DeviceInfos.Count != 0)
                    {
                        try
                        {
                            foreach (var model in DeviceInfos)
                            {
                                if (!model.IsSocket || model.Index == 999) { continue; }
                                try
                                {
                                    var pingReply = ping.Send(model.IPCOM, 500);
                                    model.ICMPResult = pingReply.Status;
                                    model.IsOnline = model.ICMPResult == IPStatus.Success;
                                    model.PingNGCount = model.ICMPResult == IPStatus.Success ? 0 : model.PingNGCount + 1;
                                    if (model.PingNGCount == 3 && model.Index != 999)
                                    {
                                        StringBuilder sb = new StringBuilder();
                                        sb.Append(string.Format("{0}:[{1}] ", _langProvider.GetLang("Station"), model.TaskName));
                                        sb.Append(string.Format("{0}:[{1}] ", _langProvider.GetLang("Index"), model.Index));
                                        sb.Append(string.Format("{0}:[{1}] ", "IP", model.IPCOM));
                                        sb.Append(string.Format("{0}:[{1}] ", "Port", model.Port));
                                        sb.Append(_langProvider.GetToolTip("DeviceCommunicationException"));
                                        _runLog.LogRun(sb.ToString(), Core.Enums.LogLevel.Error);
                                        if (temporaryParameter.GetStatusSoftware())
                                        {
                                            taskManager.StopTask();
                                            while (temporaryParameter.GetStatusSoftware())
                                            {
                                                Thread.Sleep(400);
                                            }
                                            deviceFactory.Disconnect();
                                        }
                                        continue;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    StringBuilder sb = new StringBuilder();
                                    sb.Append(string.Format("{0}:[{1}] ", _langProvider.GetLang("Station"), model.TaskName));
                                    sb.Append(string.Format("{0}:[{1}] ", _langProvider.GetLang("Index"), model.Index));
                                    sb.Append(string.Format("{0}:[{1}] ", "IP", model.IPCOM));
                                    sb.Append(string.Format("{0}:[{1}] ", "Port", model.Port));
                                    sb.Append(string.Format("Exception:{0}", ex));
                                    _runLog.LogRun(sb.ToString(), Core.Enums.LogLevel.Error);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            _runLog.LogRun($"设备Ping 异常信息：{ex}", Core.Enums.LogLevel.Error);
                        }
                    }
                    Thread.Sleep(500);
                }
            });
        }
        public bool Save()
        {
            Dictionary<string, object> data = new Dictionary<string, object>();
            data.Add("DeviceInfos", DeviceInfos);
            return Save(System.Text.Json.JsonSerializer.Serialize(data, OptionsHelper.Options));
        }

        /// <summary>
        /// 添加单设备
        /// </summary>
        /// <param name="index">索引</param>
        /// <param name="ipcom"></param>
        /// <param name="port"></param>
        /// <param name="baudRate"></param>
        /// <param name="isSocket"></param>
        /// <param name="type"></param>
        public void Add(string ipcom, int port, int baudRate, bool isSocket, bool isudp, DeviceTypes type, string taskTypes, string serviceName)
        {
            DeviceInfos.Add(new DeviceInfoModel()
            {
                TaskName = taskTypes,
                ServiceName = serviceName,
                Index = 1,
                IPCOM = ipcom,
                Port = port,
                IsSocket = isSocket || isudp,
                DeviceType = type,
                IsUDP = isudp,
            });
        }

        /// <summary>
        /// 生成IP地址
        /// </summary>
        /// <param name="startAddress">开始地址</param>
        /// <param name="startPort">开始端口</param>
        /// <param name="endAddress">结束地址</param>
        /// <param name="endPort">结束端口</param>
        public void AddDevices(string startAddress, string startPort, string endAddress, string endPort, bool isSocket, bool isudp, DeviceTypes type, string taskTypes, string serviceName)
        {
            string[] str = new string[] { startAddress, startPort, endAddress, endPort };
            GenerateAddress(str, type, taskTypes, serviceName,
                GetAddressPortInt(str),
                DeviceInfos, isudp);
        }

        #region 计算IP地址差与生成
        private void GenerateAddress(string[] str, DeviceTypes deviceType, string taskTypes, string serviceName, CalculateWeighingAddress calculate, ObservableCollection<DeviceInfoModel> servers, bool isudp)
        {
            if (string.IsNullOrEmpty(str[0]) || string.IsNullOrEmpty(str[1]) || string.IsNullOrEmpty(str[2]) || string.IsNullOrEmpty(str[3])) return;
            int count = 0;
            if (calculate.IPLength == 0)
            {
                for (int i = 0; i <= calculate.PortLength; i++)
                {
                    count = i + 1;
                    int port = calculate.StartPort + i;
                    if (servers.Any(x => x.IsSocket && x.TaskName == taskTypes
                    && x.ServiceName == serviceName
                    && x.Index == count
                    && x.DeviceType == deviceType
                    && x.Port == port
                    && x.IsUDP == isudp
                    && x.IPCOM == calculate.Address))
                    {
                        continue;
                    }
                    servers.Add(new DeviceInfoModel()
                    {
                        TaskName = taskTypes,
                        ServiceName = serviceName,
                        IPCOM = calculate.Address,
                        Port = port,
                        DeviceType = deviceType,
                        IsSocket = true,
                        IsUDP = isudp,
                        Index = count,
                    });
                }
                return;
            }
            for (int i = 0; i <= calculate.IPLength; i++)
            {
                count = i + 1;
                string ip = $"{calculate.StrArray[0]}.{calculate.StrArray[1]}.{calculate.StrArray[2]}.{calculate.StartIP + i}";
                if (servers.Any(x => x.IsSocket && x.TaskName == taskTypes
                && x.ServiceName == serviceName
                && x.Index == count
                && x.DeviceType == deviceType
                && x.Port == calculate.StartPort
                && x.IPCOM == ip))
                {
                    continue;
                }
                servers.Add(
                    new DeviceInfoModel()
                    {
                        TaskName = taskTypes,
                        ServiceName = serviceName,
                        IPCOM = ip,
                        Port = calculate.StartPort,
                        DeviceType = deviceType,
                        IsSocket = true,
                        IsUDP = isudp,
                        Index = count,
                    });
            }
        }

        private CalculateWeighingAddress GetAddressPortInt(string[] str)
        {
            string[] strArray = str[0].Split('.');
            var obj = new CalculateWeighingAddress()
            {
                Address = str[0],
                StrArray = strArray,
                StartIP = Convert.ToInt32(strArray[3]),
                StartPort = Convert.ToInt32(str[1]),
                EndIP = Convert.ToInt32(str[2].Split('.')[3]),
                EndPort = Convert.ToInt32(str[3])
            };
            obj.IPLength = obj.EndIP - obj.StartIP;
            obj.PortLength = obj.EndPort - obj.StartPort;
            return obj;
        }

        private class CalculateWeighingAddress
        {
            /// <summary>
            /// IP地址
            /// </summary>
            public string Address { get; set; }
            /// <summary>
            /// 开始地址最后一位
            /// </summary>
            public int StartIP { get; set; }
            /// <summary>
            /// 结束地址最后一位
            /// </summary>
            public int EndIP { get; set; }
            /// <summary>
            /// 开始端口号
            /// </summary>
            public int StartPort { get; set; }
            /// <summary>
            /// 结束端口号
            /// </summary>
            public int EndPort { get; set; }
            /// <summary>
            /// IP差
            /// </summary>
            public int IPLength { get; set; }
            /// <summary>
            /// 端口号差
            /// </summary>
            public int PortLength { get; set; }
            /// <summary>
            /// 分割IP地址后的数组
            /// </summary>
            public string[] StrArray { get; set; }
        }
        #endregion

        public List<DeviceInfoModel> GetDevices()
        {
            return DeviceInfos.ToList();
        }

        public void DeleteDevice(DeviceInfoModel model)
        {
            var device = DeviceInfos.FirstOrDefault(x => x.DeviceType == model.DeviceType
            && x.IsSocket == model.IsSocket
            && x.IsUDP == model.IsUDP
            && x.ServiceName == model.ServiceName
            && x.TaskName == model.TaskName
            && x.StationNumber == model.StationNumber
            && x.IPCOM == model.IPCOM
            && x.Port == model.Port);
            if (device != null)
            {
                DeviceInfos.Remove(device);
            }
        }
    }
}
